package jgroups.demos;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Vector;

import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;

/**
 * Demonstrates the membership service. Each member is represented by a rectangle that contains the
 * addresses of all the members. The coordinator (= oldest member in the group) is painted in blue.
 * New members can be started; all existing members will update their graphical appearance to reflect
 * the new membership. When the coordinator itself is killed, another one will take over (the next in rank).<p>
 * A nice demo is to start a number of Topology instances at the same time. All of them will be blue (all are
 * coordinators since they don't find each other). Then the MERGE2 protocol sets in and only one will retain
 * its coordinator role.
 * @todo Needs to be ported to Swing.
 * @author Bela Ban
 */
public class Topology extends Frame implements WindowListener {
	private final Vector members = new Vector();
	private final Font myFont;
	private final FontMetrics fm;
	private final Color node_color = new Color(250, 220, 100);
	private boolean coordinator = false;
	private static final int NormalStyle = 0;
	private static final int CheckStyle = 1;
	private JChannel channel;
	private Object my_addr = null;
	private static final String channel_name = "FD-Heartbeat";

	public Topology() {
		addWindowListener(this);
		//g=getGraphics();
		fm = getFontMetrics(new Font("Helvetica", Font.PLAIN, 12));
		myFont = new Font("Helvetica", Font.PLAIN, 12);

	}

	public void addNode(Object member) {
		Object tmp;
		for (int i = 0; i < members.size(); i++) {
			tmp = members.elementAt(i);
			if (member.equals(tmp))
				return;
		}
		members.addElement(member);
		repaint();
	}

	public void removeNode(Object member) {
		Object tmp;
		for (int i = 0; i < members.size(); i++) {
			tmp = members.elementAt(i);
			if (member.equals(tmp)) {
				members.removeElement(members.elementAt(i));
				break;
			}
		}
		repaint();
	}

	public void drawNode(Graphics g, int x, int y, String label, int style) {
		Color old = g.getColor();
		int width, height;
		width = fm.stringWidth(label) + 10;
		height = fm.getHeight() + 5;

		g.setColor(node_color);

		g.fillRect(x, y, width, height);
		g.setColor(old);
		g.drawString(label, x + 5, y + 15);
		g.drawRoundRect(x - 1, y - 1, width + 1, height + 1, 10, 10);
		if (style == CheckStyle) {
			g.drawRoundRect(x - 2, y - 2, width + 2, height + 2, 10, 10);
			g.drawRoundRect(x - 3, y - 3, width + 3, height + 3, 10, 10);
		}
	}

	public void drawTopology(Graphics g) {
		int x = 20, y = 50;
		String label;
		Dimension box = getSize();
		Color old = g.getColor();

		if (coordinator) {
			g.setColor(Color.cyan);
			g.fillRect(11, 31, box.width - 21, box.height - 61);
			g.setColor(old);
		}

		g.drawRect(10, 30, box.width - 20, box.height - 60);
		g.setFont(myFont);

		for (int i = 0; i < members.size(); i++) {
			label = members.elementAt(i).toString();
			drawNode(g, x, y, label, NormalStyle);
			y += 50;
		}

	}

	public void paint(Graphics g) {
		drawTopology(g);
	}

	/* ------------ Callbacks ------------- */

	public void coordinatorChosen() {
		coordinator = true;
		repaint();
	}

	public void windowActivated(WindowEvent e) {
	}

	public void windowClosed(WindowEvent e) {
	}

	public void windowClosing(WindowEvent e) {
		setVisible(false);
		channel.close();
	}

	public void windowDeactivated(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}

	public void start() throws Exception {
		String props = "udp.xml";

		channel = new JChannel(props);

		channel.setReceiver(new ReceiverAdapter() {
			public void viewAccepted(View view) {
				setInternalState(view.getMembers());
			}

			public void setInternalState(java.util.List<Address> mbrs) {
				members.removeAllElements();
				for (Address mbr : mbrs)
					addNode(mbr);
				coordinator = mbrs.size() <= 1
						|| (mbrs.size() > 1 && mbrs.iterator().next()
								.equals(my_addr));
				repaint();
			}
		});
		channel.connect(channel_name);
		my_addr = channel.getAddress();
		if (my_addr != null)
			setTitle(my_addr.toString());
		pack();
		setVisible(true);
	}

	public static void main(String[] args) {
		try {
			Topology top = new Topology();
			top.setLayout(null);
			top.setSize(240, 507);
			top.start();
		} catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
			System.exit(0);
		}
	}

}
